module Main where
import Data.List

-- Node number for unique variable naming (such as line number in code)
data Method = Method {name :: String, params :: [Parameter], body :: Statement}

data Parameter = Parameter {paramName :: String, paramType :: Type}

data Type = FileType 
          | IntType 
          | ListType
data Statement = Block [Statement]
               | VarDeclaration String Type
               | Iterate {iterVar :: String, iter :: Iterator, iterateBody :: Statement}
               | Sort String
               | TempFileDeclaration String
               | WriteToFile String String
               | ListAppend {list :: String, item :: String}
               | Assignment {var :: String, exp :: Expression}
               | Writing {file :: String, stmt :: Statement}
               | DoWhile Statement
               | If {condition :: Expression, ifbranch :: Statement, elsebranch :: Statement}
               | Break
               | Next {iterator :: String}
               | Release {iterator :: String}
               | Return Expression

data Iterator = FileByIterator {iteratedByFile :: String, className :: String, by :: Int}
              | FileIterator {iteratedFile :: String}
              | ListIterator {iteratedList :: String}

data Expression = IteratorExpression Iterator 
                | VariableReference String
                | Null 
                | Equals Expression Expression 
                | NotEquals Expression Expression 
                | Greater Expression Expression  
                | MethodCall {methodName :: String, args :: [String]}
                | Or Expression Expression 
                | Not Expression 
                | HasData Iterator
                | Released Iterator
                | Data Iterator
              
generateParam :: Parameter -> String
generateParam p = (generateType (paramType p)) ++ " " ++ (paramName p)

generateParamList :: [Parameter] -> String
generateParamList p = case p of 
  param:[]     -> generateParam param
  param:params -> (generateParam param) ++ ", " ++ (generateParamList params)
  []           -> ""

generateMethod m = "public void " ++ (name m) ++ "(" ++ (generateParamList (params m)) ++ ")" ++ (generateStatement (body m))

generateType t = case t of
  FileType -> "File"
  IntType -> "int"
  ListType -> "List"

generateStatement s = case s of
  Block stmts -> foldr (++) "" (map generateStatement stmts) 
  Break -> "break;\n"
  Iterate varName iter body 
    -> "for (BlockIterator iter = " ++ (generateIterator iter)
    ++ "; !iter.done(); iter.next()) {\nbyte[] " ++ varName ++ " = iter.data();\n"
    ++ (generateStatement body) ++ "}\n"
  Writing file body 
    -> let streamName = file ++ "Stream" 
    in "OutputStream " ++ streamName ++ " = new FileOutputStream(" ++ file ++ ");\n"
    ++ (generateStatement body) ++ streamName ++ ".close();\n"
  WriteToFile file buffer -> file ++ "Stream.write(" ++ buffer ++ ");\n" 
  TempFileDeclaration fileName 
    -> "File " ++ fileName ++ " = new File(\"tmp_" ++ fileName ++ "\");\n"
 
generateIterator i = case i of 
  FileByIterator iteratedByFile iteratedByClass by -> 
    "new BlockIterator(" ++ iteratedByFile ++ ", " ++ iteratedByClass ++ ".size(), " ++ (show by) ++ ")"
  FileIterator iteratedFile -> "Iterators.fileIterator(" ++ iteratedFile ++ ")"
  ListIterator iteratedList -> "stub"

main = putStrLn (generateStatement (Block [(TempFileDeclaration "out"), (Writing "fileVar" (Iterate "buffer" (FileByIterator "file" "Data" 1) (WriteToFile "out" "buffer")))]))
--main = putStrLn(generateIterator (FileByIterator "file" "Data" 1))
